<template>
  <div :class="classes">
    <button
      type="button"
      :class="arrowClasses"
      class="left"
      @click="arrowEvent(-1)"
    >
      <Icon type="ios-arrow-back" />
    </button>
    <div :class="[prefixCls + '-list']">
      <div
        ref="originTrack"
        :class="[prefixCls + '-track', showCopyTrack ? '' : 'higher']"
        :style="trackStyles"
        @click="handleClick('currentIndex')"
      >
        <slot />
      </div>
      <div
        v-if="loop"
        ref="copyTrack"
        :class="[prefixCls + '-track', showCopyTrack ? 'higher' : '']"
        :style="copyTrackStyles"
        @click="handleClick('copyTrackIndex')"
      />
    </div>
    <button
      type="button"
      :class="arrowClasses"
      class="right"
      @click="arrowEvent(1)"
    >
      <Icon type="ios-arrow-forward" />
    </button>
    <ul :class="dotsClasses">
      <template v-for="n in slides.length">
        <li
          :class="[n - 1 === currentIndex ? prefixCls + '-active' : '']"
          @click="dotsEvent('click', n - 1)"
          @mouseover="dotsEvent('hover', n - 1)"
        >
          <button
            type="button"
            :class="[radiusDot ? 'radius' : '']"
          />
        </li>
      </template>
    </ul>
  </div>
</template>
<script>
import Icon from '../icon/icon.vue'
import { getStyle, oneOf } from '../../utils/assist'
import { on, off } from '../../utils/dom'

const prefixCls = 'ivu-carousel'

export default {
  name: 'Carousel',
  components: { Icon },
  props: {
    arrow: {
      type: String,
      default: 'hover',
      validator (value) {
        return oneOf(value, ['hover', 'always', 'never'])
      }
    },
    autoplay: {
      type: Boolean,
      default: false
    },
    autoplaySpeed: {
      type: Number,
      default: 2000
    },
    loop: {
      type: Boolean,
      default: false
    },
    easing: {
      type: String,
      default: 'ease'
    },
    dots: {
      type: String,
      default: 'inside',
      validator (value) {
        return oneOf(value, ['inside', 'outside', 'none'])
      }
    },
    radiusDot: {
      type: Boolean,
      default: false
    },
    trigger: {
      type: String,
      default: 'click',
      validator (value) {
        return oneOf(value, ['click', 'hover'])
      }
    },
    value: {
      type: Number,
      default: 0
    },
    height: {
      type: [String, Number],
      default: 'auto',
      validator (value) {
        return value === 'auto' || Object.prototype.toString.call(value) === '[object Number]'
      }
    }
  },
  data () {
    return {
      prefixCls: prefixCls,
      listWidth: 0,
      trackWidth: 0,
      trackOffset: 0,
      trackCopyOffset: 0,
      showCopyTrack: false,
      slides: [],
      slideInstances: [],
      timer: null,
      ready: false,
      currentIndex: this.value,
      trackIndex: this.value,
      copyTrackIndex: this.value,
      hideTrackPos: -1 // 默认左滑
    }
  },
  computed: {
    classes () {
      return [
                    `${prefixCls}`
      ]
    },
    trackStyles () {
      // #6076
      const visibleStyle = this.trackIndex === -1 ? 'hidden' : 'visible'
      return {
        width: `${this.trackWidth}px`,
        transform: `translate3d(${-this.trackOffset}px, 0px, 0px)`,
        transition: `transform 500ms ${this.easing}`,
        visibility: visibleStyle
      }
    },
    copyTrackStyles () {
      return {
        width: `${this.trackWidth}px`,
        transform: `translate3d(${-this.trackCopyOffset}px, 0px, 0px)`,
        transition: `transform 500ms ${this.easing}`,
        position: 'absolute'
        // top: 0
      }
    },
    arrowClasses () {
      return [
                    `${prefixCls}-arrow`,
                    `${prefixCls}-arrow-${this.arrow}`
      ]
    },
    dotsClasses () {
      return [
                    `${prefixCls}-dots`,
                    `${prefixCls}-dots-${this.dots}`
      ]
    }
  },
  watch: {
    autoplay () {
      this.setAutoplay()
    },
    autoplaySpeed () {
      this.setAutoplay()
    },
    trackIndex () {
      this.updateOffset()
    },
    copyTrackIndex () {
      this.updateOffset()
    },
    height () {
      this.updatePos()
    },
    value (val) {
      //                this.currentIndex = val;
      //                this.trackIndex = val;
      this.updateTrackIndex(val)
      this.setAutoplay()
    }
  },
  mounted () {
    this.updateSlides(true)
    this.handleResize()
    this.setAutoplay()
    //            window.addEventListener('resize', this.handleResize, false);
    on(window, 'resize', this.handleResize)
  },
  methods: {
    // find option component
    findChild (cb) {
      const find = function (child) {
        const name = child.$options.componentName

        if (name) {
          cb(child)
        } else if (child.$children.length) {
          child.$children.forEach((innerChild) => {
            find(innerChild, cb)
          })
        }
      }

      if (this.slideInstances.length || !this.$children) {
        this.slideInstances.forEach((child) => {
          find(child)
        })
      } else {
        this.$children.forEach((child) => {
          find(child)
        })
      }
    },
    // copy trackDom
    initCopyTrackDom () {
      this.$nextTick(() => {
        this.$refs.copyTrack.innerHTML = this.$refs.originTrack.innerHTML
      })
    },
    updateSlides (init) {
      const slides = []
      let index = 1

      this.findChild((child) => {
        slides.push({
          $el: child.$el
        })
        child.index = index++

        if (init) {
          this.slideInstances.push(child)
        }
      })

      this.slides = slides
      this.updatePos()
    },
    updatePos () {
      this.findChild((child) => {
        child.width = this.listWidth
        child.height = typeof this.height === 'number' ? `${this.height}px` : this.height
      })

      this.trackWidth = (this.slides.length || 0) * this.listWidth
    },
    // use when slot changed
    slotChange () {
      this.$nextTick(() => {
        this.slides = []
        this.slideInstances = []

        this.updateSlides(true, true)
        this.updatePos()
        this.updateOffset()
      })
    },
    handleResize () {
      this.listWidth = parseInt(getStyle(this.$el, 'width'))
      this.updatePos()
      this.updateOffset()
    },
    updateTrackPos (index) {
      if (this.showCopyTrack) {
        this.trackIndex = index
      } else {
        this.copyTrackIndex = index
      }
    },
    updateTrackIndex (index) {
      if (this.showCopyTrack) {
        this.copyTrackIndex = index
      } else {
        this.trackIndex = index
      }
      this.currentIndex = index
    },
    add (offset) {
      // 获取单个轨道的图片数
      const slidesLen = this.slides.length
      // 如果是无缝滚动，需要初始化双轨道位置
      if (this.loop) {
        if (offset > 0) {
          // 初始化左滑轨道位置
          this.hideTrackPos = -1
        } else {
          // 初始化右滑轨道位置
          this.hideTrackPos = slidesLen
        }
        this.updateTrackPos(this.hideTrackPos)
      }
      // 获取当前展示图片的索引值
      const oldIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex
      let index = oldIndex + offset
      while (index < 0) index += slidesLen
      if (((offset > 0 && index === slidesLen) || (offset < 0 && index === slidesLen - 1)) && this.loop) {
        // 极限值（左滑：当前索引为总图片张数， 右滑：当前索引为总图片张数 - 1）切换轨道
        this.showCopyTrack = !this.showCopyTrack
        this.trackIndex += offset
        this.copyTrackIndex += offset
      } else {
        if (!this.loop) index = index % this.slides.length
        this.updateTrackIndex(index)
      }
      this.currentIndex = index === this.slides.length ? 0 : index
      this.$emit('on-change', oldIndex, this.currentIndex)
      this.$emit('input', this.currentIndex)
    },
    arrowEvent (offset) {
      this.setAutoplay()
      this.add(offset)
    },
    dotsEvent (event, n) {
      const curIndex = this.showCopyTrack ? this.copyTrackIndex : this.trackIndex
      const oldCurrentIndex = this.currentIndex
      if (event === this.trigger && curIndex !== n) {
        this.updateTrackIndex(n)
        this.$emit('on-change', oldCurrentIndex, this.currentIndex)
        this.$emit('input', n)
        // Reset autoplay timer when trigger be activated
        this.setAutoplay()
      }
    },
    setAutoplay () {
      window.clearInterval(this.timer)
      if (this.autoplay) {
        this.timer = window.setInterval(() => {
          this.add(1)
        }, this.autoplaySpeed)
      }
    },
    updateOffset () {
      this.$nextTick(() => {
        /* hack: revise copyTrack offset (1px) */
        const ofs = this.copyTrackIndex > 0 ? -1 : 1
        this.trackOffset = this.trackIndex * this.listWidth
        this.trackCopyOffset = this.copyTrackIndex * this.listWidth + ofs
      })
    },
    handleClick (type) {
      this.$emit('on-click', this[type])
    }
  },
  beforeUnmont () {
    //            window.removeEventListener('resize', this.handleResize, false);
    off(window, 'resize', this.handleResize)
  }
}
</script>
